
#include <iostream>
#include <cstdio>
#include "Graphic.h"
#include "../Synaptic/Surface.h"

namespace Engine {

namespace Resource {

Graphic Graphic::merge(GraphicList gfxList, std::string resultName){
	int biggestWidth = 1, biggestHeight = 1;
	for (GraphicList::iterator i(gfxList.begin()); i != gfxList.end(); i++) {
		if ((*i)->position.x + (*i)->frame.w > biggestWidth) biggestWidth = (*i)->position.x + (*i)->frame.w;
		if ((*i)->position.y + (*i)->frame.h > biggestHeight) biggestHeight = (*i)->position.y + (*i)->frame.h;
	}
	return merge(gfxList, biggestWidth, biggestHeight, resultName);
}

// Merge a list of graphics into a single graphic
Graphic Graphic::merge(GraphicList gfxList, unsigned int resultWidth, unsigned int resultHeight, std::string resultName){
	if (resultWidth == 0 || resultHeight == 0) {
		// error?
	}
	/* zOrdering sort */
	gfxList.sort(Misc::lessThan<Resource::Graphic>);
	
	/* Create a new surface with the given size */
	SDL_Surface * resultSurface = Engine::Synaptic::Surface::create(resultWidth, resultHeight, SDL_SWSURFACE | SDL_SRCALPHA);
	//SDL_Surface * mergeSurface = 0;
	
	/* Create a new graphic with the blank surface */
	Graphic retGfx(resultSurface, resultName);
	
	/* Blit all graphics in the graphic list */
	for (GraphicList::iterator i(gfxList.begin()); i != gfxList.end(); i++)
		Engine::Renderer::render(*(*i), retGfx);
	/* Return this new graphic */
	return retGfx;
}

Graphic::Graphic(std::string name, std::string package, Sint16 zOrder){
  	surfaceName = "./graphic/" + package + "/" + name + ".png";
	try {
		surface = Synaptic::Surface::getSurface(surfaceName);
	} catch(const char * error) {
		std::cerr << "Error loading graphic " << name << " from package " << package << ": " << error << std::endl;
	}
	
	// Graphical default parameters
	xScreenOffset = yScreenOffset = xFrameOffset = yFrameOffset = 0;
	position.x = position.y = position.w = position.h = 0;
	frame.x = frame.y = 0;
	frame.w = surface->w;
	frame.h = surface->h;
	rotation = 0;
	this->zOrder = zOrder;
}

Graphic::Graphic(const Graphic & source, Sint16 zOrder){
  	this->surface = source.surface;
	this->surfaceName = source.surfaceName;
	this->rotation = source.rotation;
	this->frame.x = source.frame.x;
	this->frame.y = source.frame.y;
	this->frame.w = source.frame.w;
	this->frame.h = source.frame.h;
	this->position.x = this->position.y = this->position.w = this->position.h = 0;
	this->xScreenOffset = this->yScreenOffset = this->xFrameOffset = this->yFrameOffset = 0;
	this->zOrder = zOrder;
}

Graphic::Graphic(SDL_Surface * surface, std::string surfaceName, Sint16 zOrder) {
	/* TODO: make type SDL_Surface independent */
	/*surfaceName = "./graphic/" + package + "/" + name + ".png"; */
	try {
		this->surface = Synaptic::Surface::getSurface(surfaceName);
		if (this->surface != surface) {
			Synaptic::Surface::setSurface(surfaceName, surface);
			this->surface = surface;
		}
	} catch(const char * error) {
		this->surface = surface;
		Synaptic::Surface::setSurface(surfaceName, surface);
		std::cout << "(Graphic) Error in " << surfaceName << ": " << error << std::endl;
		SDL_ClearError();
	}
	
	// Graphical default parameters
	xScreenOffset = yScreenOffset = xFrameOffset = yFrameOffset = 0;
	position.x = position.y = position.w = position.h = 0;
	frame.x = frame.y = 0;
	frame.w = surface->w;
	frame.h = surface->h;
	rotation = 0;
	this->zOrder = zOrder;
}

Graphic::~Graphic(){
}

// Slice the actual or the given frame and put into a new graphic

Graphic Graphic::slice(int row, int column) const {
  	// prepare for calling the frame slicer 
	if (column == -1) column = frame.x/frame.w;
	if (row == -1) row = frame.y/frame.h;
	Misc::Frame sliceFrame;
	sliceFrame.x = this->frame.w * column;
	sliceFrame.y = this->frame.h * row;
	sliceFrame.w = this->frame.w;
	sliceFrame.h = this->frame.h;
	return this->slice(sliceFrame);
}

// Slice the actual or the given frame and put into a new graphic

Graphic Graphic::slice(const Misc::Frame & sliceFrame) const {
  	char sliceStr[20];
	Graphic sliced(*this);
	std::sprintf(sliceStr, ":s=%dx%d-%dx%d", sliceFrame.x, sliceFrame.y, sliceFrame.w, sliceFrame.h);
	sliced.surfaceName = surfaceName + sliceStr;
	sliced.surface = Synaptic::Surface::slice(surfaceName,  sliceFrame, sliced.surfaceName);
	sliced.frame.x = 0;
	sliced.frame.y = 0;
	sliced.frame.w = sliceFrame.w;
	sliced.frame.h = sliceFrame.h;
	return sliced;
}

// Resize the graphic to a given factor, including its frame size.
Graphic Graphic::resize(double xFactor, double yFactor) const {
	char resizeStr[20];
  	if (xFactor == 1.0f && yFactor == 1.0f) {
		return (Graphic(*this));
	}
	Graphic resized(*this);
	std::sprintf(resizeStr, ":x=%.2fx%.2f/", xFactor, yFactor);
	resized.surfaceName = surfaceName + resizeStr;
	resized.surface = Synaptic::Surface::resize(surfaceName, xFactor, yFactor, resized.surfaceName);
	resized.frame.x = frame.x;
	resized.frame.y = frame.y;
	resized.frame.w = (Uint16)(frame.w * xFactor);
	resized.frame.h = (Uint16)(frame.h * yFactor);
	return resized;
}

// Resize the graphic to a given size, including its frame size.
Graphic Graphic::resize(int xSize, int ySize) const {
  	double xFactor, yFactor;
	xFactor = (xSize*1.0)/surface->w;
	yFactor = (ySize*1.0)/surface->h;
	return this->resize(xFactor, yFactor);
}


} // namespace Engine::Resource

} // namespace Engine
